/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved.
 * secPaver is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package builder

import (
	"fmt"
	"gitee.com/openeuler/secpaver/common/project"
	"gitee.com/openeuler/secpaver/engine/selinux/pkg/secontext"
	"gitee.com/openeuler/secpaver/engine/selinux/pkg/serule"
)

func (g *ruleGenerator) generateFileRules(subType string, objPaths, actions []string) ([]serule.Rule, error) {
	var rules []serule.Rule

	for _, objPath := range objPaths {
		var objClass secontext.FileClass
		var objType string

		info, ok := g.fileInfo[objPath]
		if !ok || info == nil || info.fileContext == nil { // not specify context, use default
			objClass = secontext.ComFile
			if info != nil {
				objClass = info.class
			}

			fctx := g.handle.MatchFileContext(objPath, objClass)
			if fctx == nil {
				return nil, fmt.Errorf("fail to match context for %s", objPath)
			}

			objType = fctx.Context.Type
		} else {
			objClass = info.class
			objType = info.fileContext.Context.Type
		}

		for _, action := range actions {
			var rs []serule.Rule
			var err error

			switch action {
			case project.ActionFileCreate:
				fctx := g.handle.MatchFileContext(getDir(objPath), secontext.DirFile)
				if fctx == nil {
					return nil, fmt.Errorf("fail to match context for %s", objPath)
				}

				rs, err = fileCreateRules(subType, objType, fctx.Context.Type, objClass)
			case project.ActionFileRead:
				rs, err = fileReadRules(subType, objType, objClass)
			case project.ActionFileWrite:
				rs, err = fileWriteRules(subType, objType, objClass)
			}

			if err != nil {
				return nil, err
			}

			rules = append(rules, rs...)
		}
	}

	return rules, nil
}

func fileCreateRules(subType, objType, objDirType string, objClass secontext.FileClass) ([]serule.Rule, error) {
	// generate create avc rules
	fileCreateRules, err := serule.CreateFileAllowRules(subType, objType, objClass, baseFileCreatePermsSet)
	if err != nil {
		return nil, err
	}

	// generate write rule for dir
	dirWriteRules, err := serule.CreateFileAllowRules(subType, objDirType, secontext.DirFile, dirFileWritePermsSet)
	if err != nil {
		return nil, err
	}

	return append(fileCreateRules, dirWriteRules...), nil
}

func fileReadRules(subType, objType string, objClass secontext.FileClass) ([]serule.Rule, error) {
	// generate create avc rules
	return serule.CreateFileAllowRules(subType, objType, objClass, baseFileReadPermsSet)
}

func fileWriteRules(subType, objType string, objClass secontext.FileClass) ([]serule.Rule, error) {
	// generate create avc rules
	return serule.CreateFileAllowRules(subType, objType, objClass, baseFileWritePermsSet)
}

// PermsSetMap is the map change secPaver permission keywords to the selinux permission set
type PermsSetMap map[string][]string

var fileTypeMap = map[string]secontext.FileClass{
	project.ComFile:     secontext.ComFile,
	project.SockFile:    secontext.SockFile,
	project.LinkFile:    secontext.LnkFile,
	project.FifoFile:    secontext.FifoFile,
	project.ChrFile:     secontext.ChrFile,
	project.BlkFile:     secontext.BlkFile,
	project.DirFile:     secontext.DirFile,
	project.ExecFile:    secontext.ComFile,
	project.AllFile:     secontext.UnknownFile,
	project.UnknownFile: secontext.UnknownFile,
}

// For ease of use, secPaver packaged some common permissions
// according to different file types

var baseFileCreatePermsSet = []string{
	"open", "getattr", "setattr", "write", "create",
}

var baseFileReadPermsSet = []string{
	"open", "getattr", "read",
}

var devFileReadPermsSet = []string{
	"open", "getattr", "read", "ioctl",
}

var baseFileWritePermsSet = []string{
	"open", "getattr", "setattr", "write", "create",
}

var devFileWritePermsSet = []string{
	"open", "getattr", "setattr", "write", "ioctl",
}

var dirFileWritePermsSet = []string{
	"open", "getattr", "setattr", "write", "create",
	"add_name", "remove_name", "rmdir", "search",
}

var dirFileReadPermsSet = []string{
	"open", "getattr", "read", "search",
}

var baseFileAppendPermsSet = []string{
	"open", "getattr", "setattr", "write", "create", "append",
}

var baseFileRenamePermsSet = []string{
	"getattr", "setattr", "rename", "unlink", "link",
}

var baseFileLinkPermsSet = []string{
	"getattr", "setattr", "rename", "unlink", "link",
}

var baseFileRemovePermsSet = []string{
	"getattr", "unlink",
}

var dirFileRemovePermsSet = []string{
	"getattr", "remove_name", "rmdir",
}

var baseFileExecPermsSet = []string{
	"execute", "open", "read", "getattr",
}

var comFileExecPermsSet = []string{
	"open", "read", "execute", "execute_no_trans",
}

var baseFileIoctlPermsSet = []string{
	"ioctl",
}

var baseFileMapPermsSet = []string{
	"map",
}

var baseFileLockPermsSet = []string{
	"lock",
}

var baseFileMountonPermsSet = []string{
	"mounton",
}
